package com.apobates.forum.toro.proxy;

import com.apobates.forum.toro.AbstractMapper;
import com.apobates.forum.toro.ResultMapperTransfer;
import com.apobates.forum.toro.proxy.strategy.*;
import com.apobates.forum.toro.utils.BeanHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Proxy One 聚合属性执行器
 * 适用于: 一对一属性聚合模式,基对象通过属性targetObj获取
 *
 * @author xiaofanku@live.cn
 * @since 20220517
 */
public class ProxyMapperExecutor<E,T> extends AbstractMapper<E,T> {
    private static final Logger logger = LoggerFactory.getLogger(ProxyMapperExecutor.class);

    public ProxyMapperExecutor(Supplier<Collection<T>> origin, Class<E> className, boolean isConcurrent, boolean isMany) {
        super(origin, className, isConcurrent, isMany);
    }

    /**
     * [单实例]返回最终结果
     * @param statements 聚合属性语句
     * @return
     */
    public Optional<E> get(MapperAttrStatements statements){
        return this.get(statements, this.getMapperStrategy());
    }

    /**
     * [单实例]返回最终结果
     * @param statements 聚合属性语句
     * @param strategy 聚合策略
     * @return
     */
    public Optional<E> get(MapperAttrStatements statements, MapperStrategy strategy){
        if(isMany()) {
            return Optional.empty();
        }
        T selectIns = new ArrayList<>(getOrigin().get()).get(0);
        E targetIns = BeanHelper.buildProxyInstance(getClassName(), selectIns);
        strategy.consumer(statements.getSelectMap().stream(), targetIns, selectIns);
        return Optional.ofNullable(targetIns);
    }

    /**
     * [单实例&集合]设置最终结果的消费函数
     * @param consumer 消费函数/前置处理
     * @return
     */
    public Builder<E> consume(Consumer<E> consumer){
        ResultMapperTransfer<E,E> transfer = new ResultMapperTransfer<>(){
            @Override
            public Consumer<E> getConsumer() {
                return consumer;
            }
            @Override
            public Function<E, E> getFormater() {
                return Function.identity();
            }
        };
        return new Builder<>(transfer);
    }

    /**
     * [单实例&集合]设置最终结果的映射函数
     * @param formater 映射函数/后置转换
     * @return
     * @param <F>
     */
    public <F> Builder<F> format(Function<E,F> formater){
        ResultMapperTransfer<E,F> transfer = new ResultMapperTransfer<>(){
            @Override
            public Consumer<E> getConsumer() {
                return (E ins)->logger.info("ProxyMapperExecutor Default Consumer");
            }
            @Override
            public Function<E, F> getFormater() {
                return formater;
            }
        };
        return new Builder<>(transfer);
    }

    /**
     * [单实例&集合]设置最终结果的处理函数,包括: 消费函数 & 映射函数
     * @param transfer 最终结果处理函数
     * @return
     * @param <F>
     */
    public <F> Builder<F> transfer(ResultMapperTransfer<E,F> transfer){
        return new Builder<>(transfer);
    }

    /**
     * [实例集合]返回最终结果
     * @param statements 聚合属性语句
     * @return
     */
    public Collection<E> get(MapperMultiAttrStatements statements){
        return get(statements, this.getMapperManyStrategy());
    }

    /**
     * [实例集合]返回最终结果
     * @param statements 聚合属性语句
     * @param strategy 聚合策略
     * @return
     */
    public Collection<E> get(MapperMultiAttrStatements statements, MapperManyStrategy strategy){
        if(!isMany()) {
            return Collections.emptyList();
        }
        Collection<T> selectObjSet = getOrigin().get();
        if(selectObjSet.isEmpty()){
            logger.debug("[PMME]select argument collection is empty");
            return Collections.emptyList();
        }
        Set<MapperMultiAttrExpression> selectMap = statements.getSelectMap();
        // key=目标对象属性, value=目标对象的查询参数
        Map<String, Set<String>> selectArgs = new HashMap<>();
        for(MapperMultiAttrExpression mmes : selectMap) {
            final String selectAttrName = mmes.getSelectAttr();
            Set<String> args = new HashSet<>();
            for(Iterator it = selectObjSet.iterator(); it.hasNext();) {
                Object obj = it.next();
                try{
                    //args.add(BeanUtils.getProperty(obj, selectAttrName));
                    args.add(BeanHelper.getAttrVal(obj, selectAttrName).toString());
                }catch (Exception e){}
            }
            selectArgs.put(mmes.getTargetAttr(), args);
        }
        //
        Collection<E> targetObjSet = buildTargetSet(selectObjSet, getClassName());
        logger.debug(String.format("[PMME]strategy:%s, args:%d, target:%d", strategy.getClass().getSimpleName(), selectMap.size(), targetObjSet.size()));
        strategy.consumer(selectMap, targetObjSet, selectArgs);
        return targetObjSet;
    }

    /**
     * 获取内置的一对一聚合策略
     * @return
     */
    private MapperStrategy getMapperStrategy() {
        return isConcurrent()? new MapperParallelStrategy() : new MapperSerialStrategy();
    }

    /**
     * 获取内置的多对多聚合策略
     * @return
     */
    private MapperManyStrategy getMapperManyStrategy() {
        return isConcurrent()? new MapperParallelManyStrategy() : new MapperSerialManyStrategy();
    }

    private static <T,U> Collection<T> buildTargetSet(Collection<U> selectObjSet, Class<T> className) {
        Collection<T> targetObjSet = new HashSet<>();
        for(Iterator<U> it = selectObjSet.iterator(); it.hasNext();) {
            U selectObj = it.next();
            T targetObj = BeanHelper.buildProxyInstance(className, selectObj);
            if (null != targetObj) {
                targetObjSet.add(targetObj);
            }
        }
        return targetObjSet;
    }

    /**
     * 设置最终对像的处理函数
     * @param <F> 处理后的对象类型
     */
    public class Builder<F>{
        private final ResultMapperTransfer<E,F> transfer;

        private Builder(ResultMapperTransfer<E,F> transfer){
            this.transfer = transfer;
        }

        /**
         * [单实例]返回最终结果
         * @param statements 聚合属性语句
         * @return
         */
        public Optional<F> get(MapperAttrStatements statements) {
            Optional<E> r = ProxyMapperExecutor.this.get(statements);
            return this.transfer.get(r);
        }

        /**
         * [单实例]返回最终结果
         * @param statements 聚合属性语句
         * @param strategy 聚合策略
         * @return
         */
        public Optional<F> get(MapperAttrStatements statements, MapperStrategy strategy) {
            Optional<E> r = ProxyMapperExecutor.this.get(statements, strategy);
            return this.transfer.get(r);
        }

        /**
         * [实例集合]返回最终结果
         * @param statements 聚合属性语句
         * @return
         */
        public Collection<F> get(MapperMultiAttrStatements statements){
            Collection<E> r = ProxyMapperExecutor.this.get(statements);
            return this.transfer.getAll(r);
        }

        /**
         * [实例集合]返回最终结果
         * @param statements 聚合属性语句
         * @param strategy 聚合策略
         * @return
         */
        public Collection<F> get(MapperMultiAttrStatements statements, MapperManyStrategy strategy){
            Collection<E> r = ProxyMapperExecutor.this.get(statements, strategy);
            return this.transfer.getAll(r);
        }
    }
}
